#include "ColladaModel.h"

namespace lib3dw
{
	ColladaModel::ColladaModel(char* modelFilePath)
	{
		ArghhhNoConstructorChaining(modelFilePath);
	}
	
	ColladaModel::ColladaModel(char* modelFilePath, Box newBoundingBox)
	{
		ArghhhNoConstructorChaining(modelFilePath);
		boundingBox = newBoundingBox;
	}
	
	void ColladaModel::ArghhhNoConstructorChaining(char* modelFilePath)
	{
		FCDocument* colladaDocument;
		fstring inputFilename = TO_FSTRING(modelFilePath);
		
		colladaDocument = FCollada::NewTopDocument();
		colladaDocument->LoadFromFile(inputFilename);
		
		geometry = new ColladaGeometry(colladaDocument);
		// we bind our diffuse texture always to unit 0. units above are used for shadow maps
		glActiveTexture(GL_TEXTURE0);
		textureData = new ColladaTextureData(colladaDocument);
		materialData = new ColladaMaterialData(colladaDocument);
		glslShaderData = new ColladaGLSLShaderData(colladaDocument);
		
		SAFE_DELETE(colladaDocument);
	}
	
	ColladaModel::~ColladaModel()
	{
		delete geometry;
		delete textureData;
		delete materialData;
		delete glslShaderData;
	}

	void ColladaModel::SetTranslation(float x, float y, float z)
	{
		geometry->translationVector->x = x;
		geometry->translationVector->y = y;
		geometry->translationVector->z = z;
		if(boundingBox.parameters != NULL)
		{
			boundingBox.SetTranslation(x, y, z);
		}
	}
	
	void ColladaModel::SetRotation(float angle, float x, float y, float z)
	{
		geometry->rotationVector->w = angle;
		geometry->rotationVector->x = x;
		geometry->rotationVector->y = y;
		geometry->rotationVector->z = z;
	}
	
	void ColladaModel::SetScale(float x, float y, float z)
	{
		geometry->scaleVector->x = x;
		geometry->scaleVector->y = y;
		geometry->scaleVector->z = z;
		if(boundingBox.parameters != NULL)
		{
			boundingBox.SetScale(x, y, z);
		}
	}

	void ColladaModel::SetAmbient(float r, float g, float b, float a)
	{
		materialData->ambientMaterial[0] = r;
		materialData->ambientMaterial[1] = g;
		materialData->ambientMaterial[2] = b;
		materialData->ambientMaterial[3] = a;
	}
	
	void ColladaModel::SetDiffuse(float r, float g, float b, float a)
	{
		materialData->diffuseMaterial[0] = r;
		materialData->diffuseMaterial[1] = g;
		materialData->diffuseMaterial[2] = b;
		materialData->diffuseMaterial[3] = a;
	}

	void ColladaModel::SetSpecular(float r, float g, float b, float a)
	{
		materialData->specularMaterial[0] = r;
		materialData->specularMaterial[1] = g;
		materialData->specularMaterial[2] = b;
		materialData->specularMaterial[3] = a;
	}
	
	void ColladaModel::SetShininess(GLfloat newShininess)
	{
		materialData->shininess = newShininess;
	}

	void ColladaModel::SetBoundingBox(Box newBoundingBox)
	{
		boundingBox = newBoundingBox;
	}

	const Vector3* ColladaModel::GetTranslation() const
	{
		return geometry->translationVector;
	}
	
	const Vector4* ColladaModel::GetRotation() const
	{
		return geometry->rotationVector;
	}
	
	const Vector3* ColladaModel::GetScale() const
	{
		return geometry->scaleVector;
	}
	
	const GLfloat* ColladaModel::GetDiffuse() const
	{
		return materialData->diffuseMaterial;
	}

	const GLfloat* ColladaModel::GetAmbient() const
	{
		return materialData->ambientMaterial;
	}
	
	const GLfloat* ColladaModel::GetSpecular() const
	{
		return materialData->specularMaterial;
	}

	const GLfloat* ColladaModel::GetEmission() const
	{
		return materialData->emissionMaterial;
	}
	
	const GLfloat ColladaModel::GetShininess() const
	{
		return materialData->shininess;
	}

	const std::vector<GLuint>* ColladaModel::GetShaderPasses() const
	{
		return &(glslShaderData->shaderPasses);
	}
	
	const Box ColladaModel::GetBoundingBox() const
	{
		return boundingBox;
	}

	void ColladaModel::Draw()
	{
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, &(materialData->ambientMaterial[0]));
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, &(materialData->diffuseMaterial[0]));
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &(materialData->specularMaterial[0]));
		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, &(materialData->emissionMaterial[0]));
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, materialData->shininess);
		
		glPushMatrix();
		glActiveTexture(GL_TEXTURE0);
		glEnable(GL_TEXTURE_2D);
	
		glTranslatef(geometry->translationVector->x, geometry->translationVector->y, geometry->translationVector->z);
		glRotatef(geometry->rotationVector->w, geometry->rotationVector->x, geometry->rotationVector->y, geometry->rotationVector->z);
		glScalef(geometry->scaleVector->x, geometry->scaleVector->y, geometry->scaleVector->z);
	
		GLuint ti = textureData->textures.front();
		glBindTexture(GL_TEXTURE_2D, ti);
		
		switch(ConfigSingleton::Instance()->GetRenderMode())
		{
			case RenderImmediate:
			{
				glBegin(GL_TRIANGLES);
				for(unsigned short triangleIndex = 0; triangleIndex < geometry->triangleCount; triangleIndex++)
				{
					const Triangle *triangle = geometry->GetTriangle(triangleIndex);
					for (unsigned short i = 0; i < 3; i++)
					{
						Vertex* v = triangle->GetVertex(i);
						const Vector3* pos = v->GetPosition();
						const Vector3* normal = v->GetNormal();
						const Vector2* texCoord = v->GetTexCoord2D();
						glTexCoord2f(texCoord->x, texCoord->y);
						glNormal3f(normal->x, normal->y, normal->z);
						glVertex3f(pos->x, pos->y, pos->z);
					}
				}
				glEnd();
				break;
			}
			case RenderDisplayList:
			{
				glCallList(geometry->displayList);
				break;
			}
			case RenderVBO:
			{
			    glEnableClientState(GL_VERTEX_ARRAY); 
				glEnableClientState(GL_NORMAL_ARRAY); 
				glEnableClientState(GL_TEXTURE_COORD_ARRAY );
				glBindBufferARB(GL_ARRAY_BUFFER_ARB, geometry->vboId);
				glTexCoordPointer(2, GL_FLOAT, 8 * sizeof(float), (GLvoid*)(sizeof(float)*0));
				glNormalPointer(GL_FLOAT,  8 * sizeof(float), (GLvoid*)(sizeof(float)*2));
				glVertexPointer(3, GL_FLOAT, 8 * sizeof(float), (GLvoid*)(sizeof(float)*5));
				glDrawArrays(GL_TRIANGLES, 0, geometry->vertexCount);
				glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
				glDisableClientState(GL_VERTEX_ARRAY);
				glDisableClientState(GL_NORMAL_ARRAY);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				break;
			}
		}
		
		glDisable(GL_TEXTURE_2D);
		glPopMatrix();
	}
}